﻿using Domain.Context;
using IRepository;
using IRepository.UnitOfWork;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;

namespace Repository.Base
{
    public class BaseRepository<TEntity> : IBaseRepository<TEntity> where TEntity : class, new()
    {
        private readonly IUnitOfWork unitOfWork;

        private readonly RepositoryContext repositoryContext;

        public BaseRepository(IUnitOfWork unitOfWork)
        {
            this.unitOfWork = unitOfWork;
            this.repositoryContext = this.unitOfWork.GetRepositoryContext();
        }

        #region Query

        public TEntity QueryById(object Id)
        {
            return repositoryContext.Set<TEntity>().Find(Id);
        }

        public IEnumerable<TEntity> Query(bool isNoTracking = true)
        {
            var result = repositoryContext.Set<TEntity>().AsQueryable();
            result = isNoTracking ? result.AsNoTracking() : result;
            return result.AsEnumerable();
        }

        public IEnumerable<TEntity> Query(Expression<Func<TEntity, bool>> whereExpression, bool isNoTracking = true)
        {
            var result = repositoryContext.Set<TEntity>()
                                    .Where(whereExpression);
            result = isNoTracking ? result.AsNoTracking() : result;
            return result.AsEnumerable();
        }

        public IQueryable<TEntity> Queryable(Expression<Func<TEntity, bool>> whereExpression, bool isNoTracking = true)
        {
            var result = repositoryContext.Set<TEntity>()
                                     .Where(whereExpression);
            result = isNoTracking ? result.AsNoTracking() : result;
            return result;
        }

        public IEnumerable<TEntity> Query(Expression<Func<TEntity, bool>> whereExpression, Expression<Func<TEntity, object>> orderByExpression, bool isAsc = true, bool isNoTracking = true)
        {
            var result = repositoryContext.Set<TEntity>().Where(whereExpression);

            result = isAsc ? result.OrderBy(orderByExpression) : result.OrderByDescending(orderByExpression);

            result = isNoTracking ? result.AsNoTracking() : result;

            return result;
        }

        public IEnumerable<TEntity> Query(Expression<Func<TEntity, bool>> whereExpression, int intTop, Expression<Func<TEntity, object>> orderByExpression = null, bool isAsc = true, bool isNoTracking = true)
        {
            var result = repositoryContext.Set<TEntity>().Where(whereExpression);

            result = orderByExpression == null ? result : (isAsc ? result.OrderBy(orderByExpression) : result.OrderByDescending(orderByExpression));

            result = (isNoTracking ? result.AsNoTracking() : result).Take(intTop);

            return result;
        }
        public IEnumerable<TEntity> Query(Expression<Func<TEntity, bool>> whereExpression, int intPageIndex, int intPageSize, Expression<Func<TEntity, object>> orderByExpression = null, bool isAsc = true, bool isNoTracking = true)
        {
            var result = repositoryContext.Set<TEntity>().Where(whereExpression);

            result = orderByExpression == null ? result : (isAsc ? result.OrderBy(orderByExpression) : result.OrderByDescending(orderByExpression));

            result = result.Skip(intPageIndex * intPageSize).Take(intPageSize);

            result = (isNoTracking ? result.AsNoTracking() : result);

            return result;
        }
        #endregion

        #region Add
        public int Add(TEntity entity)
        {
            repositoryContext.Add<TEntity>(entity);

            return repositoryContext.SaveChanges();
        }
        #endregion

        #region Delete

        public bool DeleteById(object id)
        {
            var entity = repositoryContext.Set<TEntity>().Find(id);
            if (entity == null)
            {
                return false;
            }
            repositoryContext.Remove<TEntity>(entity);

            return repositoryContext.SaveChanges() > 0;
        }

        public bool Delete(TEntity entity)
        {
            repositoryContext.Remove<TEntity>(entity);

            return repositoryContext.SaveChanges() > 0;
        }

        public bool Delete(Expression<Func<TEntity, bool>> whereExpression)
        {
            var entites = repositoryContext.Set<TEntity>().Where(whereExpression);
            repositoryContext.RemoveRange(entites);
            return repositoryContext.SaveChanges() > 0;
        }

        #endregion

        #region Update

        public bool Update(TEntity entity, IEnumerable<string> updatePropertyList = null)
        {
            repositoryContext.Set<TEntity>().Attach(entity);

            var model = repositoryContext.Entry<TEntity>(entity);

            if (updatePropertyList == null)
            {
                model.State = EntityState.Modified;
            }
            else
            {
                updatePropertyList.ToList().ForEach(prop => model.Property(prop).IsModified = true);
            }
            return repositoryContext.SaveChanges() > 0;
        }
        #endregion

    }
}
